iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Software Development

手刻 Redis:用 Go 從零打造高效能高併發的記憶體資料庫系列 第 22

Day 22:RDB (Redis Database) 快照持久化原理與binary serialization設計

  • 分享至 

  • xImage
  •  

前幾天我弄好了 AOF 追加日誌。AOF 雖然資料安全,但缺點就是檔案有夠大,資料一多,重啟時 replay 指令會跑到讓人想睡覺。

所以 Redis 還有另一招:RDB (Redis Database) 快照

今天來拆 RDB 的原理,順便設計一套自己的二進位快照格式。


什麼是 RDB 快照?

RDB 持久化可以想成幫記憶體資料庫拍一張快照,把某個時間點的資料狀態寫成二進位檔案(例如 dump.rdb)。

AOF 日誌 (像指令重播記錄): "SET a 1", "DEL a", "SET a 2"
RDB 快照 (像全景照片): "a = 2"

RDB 與 AOF 的對比

特性 RDB 快照 AOF 日誌
檔案體積 通常較小 (二進位格式,適合備份與傳輸) 較大 (純文字指令追加)
恢復速度 極快 (直接加載二進位結構到記憶體) 慢 (需要依序重跑指令)
資料安全性 較低 (若當機,會丟失最後一次快照之後的資料) 高 (可配置為每秒 fsync)

實務上通常會把兩者搭在一起用:

  • 平時使用 AOF 來保證資料安全性。
  • 每天或定期備份 RDB 來做冷備份,並在主從複製(Replication)的全量同步階段,利用 RDB 來加速資料傳輸。

我的 RDB binary serialization設計

在 Go 裡要先做一版二進位快照,不一定要自己手排 struct bytes。標準庫的 encoding/gob (Go Binary) 已經能處理不少序列化工作。

為什麼選用 encoding/gob

  • 二進位格式:Gob 是一種自我說明的二進位流,比 JSON/XML 少掉不少文字標籤。
  • 類型感知:支持多態結構的序列化。
  • 使用方便:不用先自己設計一整套 bytes layout。

由於我們的 KV 引擎底層的 entry.val 儲存的是 interface{}(可以是 List 雙向鏈結、Set 的空 map、跳躍表等),Gob 預設無法序列化複雜的指針鏈結。

為此,我們在寫入前,先將其轉換為平面化的二進位安全結構(如 List 轉為 [][]byte,ZSet 轉為 []ZSetMember),然後再使用 Gob 加密:

老實說,gob 序列化剛開始讓我很頭痛,interface{} 裡塞 map 不先 register 就直接 panic,查了半天才知道要先處理這塊。

type rdbEntry struct {
	DataType DataType
	Val      interface{}
	ExpireAt time.Time
}

為了讓 Gob 識別這些 interface{} 中包裹的具體型態,我們在 code/db/rdb.go 中註冊了它們:

func init() {
	gob.Register([]byte{})
	gob.Register([][]byte{})
	gob.Register(map[string][]byte{})
	gob.Register([]string{})
	gob.Register([]ZSetMember{})
}

這層轉換很重要,因為雙向鏈結和跳躍表這種指標結構不適合直接序列化。先攤平成 slice 或 map,再交給 Gob,會安全很多。


跑起來看看

來測一下我們的 RDB 快照能不能正常產出:

# 在 redis-cli 塞點資料並手動觸發 BGSAVE
$ redis-cli
> SET hero "batman"
# 預期回覆:OK
> BGSAVE
# 預期回覆:Background saving started

這時候回終端機看目錄,應該會多出一個 dump.rdb

$ ls -lh dump.rdb
-rw-r--r--  1 sky  staff    84B Aug 13 22:45 dump.rdb

# 用 xxd 看一下二進位長怎樣,gob 序列化的痕跡都在這
$ xxd dump.rdb | head -n 3
00000000: 476f 620a 1f23 0401 02ff 8200 0102 0110  Gob..#..........
00000010: 0000 37ff 8103 0101 0852 6462 456e 7472  ..7......RdbEntr
00000020: 7901 02ff 8200 0104 0108 4461 7461 5479  y.........DataTy

總結

今天先把 RDB 和 AOF 的差異釐清,也決定用 encoding/gob 做第一版 RDB 快照模型。

明天來處理 RDB 的 Save/Load,還要補啟動時自動判斷 AOF/RDB 的流程,這塊寫起來應該滿好玩的。


上一篇
Day 21:背景 AOF 重寫 (BGREWRITEAOF) 的 Copy-on-Write 與 Go 實作
下一篇
Day 23:實作 RDB 保存與啟動時的 AOF/RDB 自動加載資料恢復
系列文
手刻 Redis:用 Go 從零打造高效能高併發的記憶體資料庫25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言